﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;

namespace Collab.Modules.Session
{
    public class DocumentTracker : IDisposable
    {
        private readonly DTE2 dte;
        private readonly IVsTextManager textManager;
        private readonly WindowEvents windowEvents;
        private readonly IVsEditorAdaptersFactoryService adapterFactory;
        private readonly SolutionEvents solutionEvents;
        private DocumentView selectedView;

        private readonly Subject<DocumentView> whenViewActivated = new Subject<DocumentView>();
        public IObservable<DocumentView> WhenViewActivated { get { return this.whenViewActivated.AsObservable(); } }

        private readonly Subject<DocumentView> whenViewClosed = new Subject<DocumentView>();
        public IObservable<DocumentView> WhenViewClosed { get { return this.whenViewClosed.AsObservable(); } }

        private readonly BehaviorSubject<DocumentView> whenSelectedViewChanged = new BehaviorSubject<DocumentView>(null);
        public IObservable<DocumentView> WhenSelectedViewChanged { get { return this.whenSelectedViewChanged.AsObservable(); } }

        public List<DocumentView> ActiveViews { get; private set; }

        public DocumentTracker(DTE2 dte, IVsTextManager textManager)
        {
            Debug.Assert(dte != null);
            Debug.Assert(textManager != null);
            this.dte = dte;
            this.textManager = textManager;
            this.ActiveViews = new List<DocumentView>();
            IComponentModel componentModelService = Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel)) as IComponentModel;
            IEnumerable<IVsEditorAdaptersFactoryService> adapterFactories = componentModelService.DefaultExportProvider.GetExportedValues<IVsEditorAdaptersFactoryService>();
            this.adapterFactory = adapterFactories.FirstOrDefault();
            this.windowEvents = this.dte.Events.WindowEvents;
            this.windowEvents.WindowActivated += windowEvents_WindowActivated;
            this.windowEvents.WindowClosing += windowEvents_WindowClosing;

            //this.solutionEvents = this.dte.Events.SolutionEvents;
            //this.solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(() =>
            //{

                
            //    IVsEnumTextViews views;
            //    this.textManager.EnumViews(null, out views);
            //    if(v
            //    this.ActiveViews.Clear();
            //    uint count = 0;
            //    views.GetCount(ref count);
            //    for (int i = 0; i < count; i++)
            //    {
            //        IVsTextView[] viewArr = new IVsTextView[1];
            //        uint dummy = 0;
            //        views.Next((uint)1, viewArr, ref dummy);
            //        IWpfTextView wpfView = this.adapterFactory.GetWpfTextView(viewArr[0]);
            //        string name = GetFileName(wpfView.TextBuffer);
            //        this.ActiveViews.Add(new DocumentView { Name = name, View = wpfView });
            //    }
            //});




        }

        void windowEvents_WindowClosing(Window window)
        {
            if (window.ProjectItem != null)
            {
                string name = GetDocumentPath(window.ProjectItem);
                var activeView = this.ActiveViews.Find(x => x.Name == name);
                if (activeView == this.selectedView)
                {
                    this.selectedView = null;
                    this.whenSelectedViewChanged.OnNext(null);
                }

                this.ActiveViews.Remove(activeView);
                this.whenViewClosed.OnNext(activeView);
            }
        }

        private void windowEvents_WindowActivated(EnvDTE.Window gotFocus, EnvDTE.Window LostFocus)
        {
            try
            {
                if (gotFocus.ProjectItem == null)
                    return;
            }
            catch (InvalidCastException)
            {
                return;
            }


            string name = GetDocumentPath(gotFocus.ProjectItem);
            var activeView = this.ActiveViews.Find(x => x.Name == name);
            if (activeView == null)
            {
                Task.Factory.StartNew(() =>
                {
                    for (int i = 0; i < 10; i++)
                    {
                        System.Threading.Thread.Sleep(50);
                        IVsTextView textView;
                        int val = this.textManager.GetActiveView(1, null, out textView);
                        if (textView == null)
                            return;
                        IWpfTextView view = this.adapterFactory.GetWpfTextView(textView);
                        Debug.WriteLine("IN ASYNC:" + GetFileName(view.TextBuffer));
                        if (GetFileName(view.TextBuffer) == gotFocus.ProjectItem.FileNames[1])
                        {
                            activeView = new DocumentView() { Name = name, View = view };
                            this.ActiveViews.Add(activeView);
                            this.selectedView = activeView;
                            this.whenSelectedViewChanged.OnNext(activeView);
                            this.whenViewActivated.OnNext(activeView);
                            return;
                        }
                    }
                });


            }
            else
            {
                if (this.selectedView != activeView)
                {
                    this.selectedView = activeView;
                    this.whenSelectedViewChanged.OnNext(activeView);
                }

            }
        }

        public void ActivateWindow(string documentName)
        {
            this.dte.ItemOperations.OpenFile(Path.Combine(GetSolutionPath(), documentName));
        }

        private string GetDocumentPath(ProjectItem document)
        {
            try
            {
                return this.dte.Solution == null ? document.Name : GetRelativePath(document.FileNames[1], GetSolutionPath());
            }
            catch (InvalidCastException)
            {
                return document.Name;
            }

        }

        private string GetSolutionPath()
        {
            var arr = this.dte.Solution.FullName.Split(System.IO.Path.DirectorySeparatorChar);
            return string.Join(System.IO.Path.DirectorySeparatorChar.ToString(), arr.Take(arr.Length - 1));
        }

        private string GetRelativePath(string fullPath, string solutionPath)
        {
            Debug.WriteLine(fullPath.Replace(solutionPath + System.IO.Path.DirectorySeparatorChar, ""));
            return fullPath.Replace(solutionPath + System.IO.Path.DirectorySeparatorChar, "");
        }

        private string GetFileName(ITextBuffer buffer)
        {
            Microsoft.VisualStudio.TextManager.Interop.IVsTextBuffer bufferAdapter;
            buffer.Properties.TryGetProperty<Microsoft.VisualStudio.TextManager.Interop.IVsTextBuffer>(typeof(Microsoft.VisualStudio.TextManager.Interop.IVsTextBuffer), out bufferAdapter);
            if (bufferAdapter != null)
            {
                Microsoft.VisualStudio.Shell.Interop.IPersistFileFormat persistFileFormat = bufferAdapter as Microsoft.VisualStudio.Shell.Interop.IPersistFileFormat;
                string ppzsFilename;
                uint iii;
                persistFileFormat.GetCurFile(out ppzsFilename, out iii);
                return ppzsFilename;
            }
            else return null;

        }

        #region IDisposable Members

        public void Dispose()
        {
            this.windowEvents.WindowActivated -= windowEvents_WindowActivated;
            this.windowEvents.WindowClosing -= windowEvents_WindowClosing;
        }

        #endregion
    }
}
